package org.jeecg.common.es.service;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.client.IndicesClient;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexResponse;
import org.jeecg.common.es.template.CssdElasticsearchRestTemplate;
import org.jeecg.common.es.EsIndexUtil;
import org.jeecg.common.es.entity.EsEntity;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.annotations.Setting;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.ResourceUtil;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.AliasQuery;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author wangzun1.vendor@sensetime.com
 * @Description:
 * @date 2022/8/10
 */
@Slf4j
public abstract class AbstractEsService {
    protected static final Cache<String, GetIndexResponse> GET_INDEX_CACHE;
    @Autowired
    protected CssdElasticsearchRestTemplate elasticsearchRestTemplate;
    @Autowired
    protected RestHighLevelClient restHighLevelClient;

    public AbstractEsService() {}

    public GetIndexResponse getIndexResponse(String realIndex) {
        GetIndexResponse value = (GetIndexResponse)GET_INDEX_CACHE.getIfPresent(realIndex);
        if (value == null) {
            this.refreshIndexCache(realIndex);
        }

        return (GetIndexResponse)GET_INDEX_CACHE.getIfPresent(realIndex);
    }

    public <T extends EsEntity> boolean existIndex(Class<T> tClass, String yyyymm) {
        String prefixIndexName = this.elasticsearchRestTemplate.getPrefixIndexName(tClass);
        String tenantId = EsIndexUtil.getTenantId();
        String relIndexName = prefixIndexName + "_" + tenantId + "_" + yyyymm;
        GetIndexResponse indexResponse = this.getIndexResponse(relIndexName);
        if (indexResponse == null) {
            return false;
        } else {
            List<String> indices = Lists.newArrayList(indexResponse.getIndices());
            return indices.contains(relIndexName);
        }
    }

    public <T extends EsEntity> boolean existAlias(Class<T> tClass) {
        IndicesClient indicesClient = this.restHighLevelClient.indices();
        GetAliasesRequest request = new GetAliasesRequest();
        request.aliases(new String[] {EsIndexUtil.getRealAlias(tClass)});

        try {
            return indicesClient.existsAlias(request, RequestOptions.DEFAULT);
        } catch (Exception var5) {
            log.error(String.format("ES查询别名失败！request=%s errorInfo=%s", request, var5), var5);
            return false;
        }
    }

    protected void refreshIndexCache(String realIndex) {
        try {
            GetIndexRequest indexRequest = new GetIndexRequest(new String[] {realIndex});
            IndicesClient indicesClient = this.restHighLevelClient.indices();
            if (indicesClient.exists(indexRequest, RequestOptions.DEFAULT)) {
                GetIndexResponse response = indicesClient.get(indexRequest, RequestOptions.DEFAULT);
                GET_INDEX_CACHE.put(realIndex, response);
            } else {
                GET_INDEX_CACHE.invalidate(realIndex);
            }

        } catch (Exception var5) {
            log.error(String.format("查询索引出现异常,indexName=%s", realIndex), var5);
            throw new JeecgBootException("ES查询异常");
        }
    }

    protected <T extends EsEntity> void createIndexByEntity(T entity) {
        String prefixIndexName = this.elasticsearchRestTemplate.getPrefixIndexName(entity.getClass());
        String tenantId = EsIndexUtil.getTenantId();
        Long createTime = entity.getSpiderTime();
        IndexCoordinates indexCoordinates = IndexCoordinates
            .of(new String[] {prefixIndexName + "_" + tenantId + "_" + DateUtils.formatDate( createTime,"yyyyMM")});
        IndexOperations indexOps = this.elasticsearchRestTemplate.indexOps(indexCoordinates);
        String realIndexName = indexCoordinates.getIndexName();

        try {
            GetIndexResponse getIndexResponse = this.getIndexResponse(realIndexName);
            if (!EsIndexUtil.existIndex(getIndexResponse, realIndexName)) {
                this.doCreate(entity, indexOps);
            }

            String alias = EsIndexUtil.getRealAlias(entity.getClass());
            if (StringUtils.isNotBlank(alias) && !EsIndexUtil.existAlias(getIndexResponse, alias)) {
                indexOps.addAlias(new AliasQuery(alias));
            }
        } catch (Exception var13) {
            log.error(String.format("创建索引出现异常,indexName=%s", realIndexName), var13);
            throw new JeecgBootException("索引创建异常");
        } finally {
            this.refreshIndexCache(realIndexName);
        }

    }

    private <T extends EsEntity> void doCreate(T entity, IndexOperations indexOps) {
        Class<? extends EsEntity> entityClass = entity.getClass();
        if (entityClass.isAnnotationPresent(Setting.class)) {
            String settingPath = ((Setting)entityClass.getAnnotation(Setting.class)).settingPath();
            if (org.springframework.util.StringUtils.hasText(settingPath)) {
                String settings = ResourceUtil.readFileFromClasspath(settingPath);
                if (org.springframework.util.StringUtils.hasText(settings)) {
                    Document document = Document.parse(settings);
                    indexOps.create(document);
                    indexOps.putMapping(indexOps.createMapping(entity.getClass()));
                } else {
                    log.error("settings.json：The content cannot be empty.");
                    throw new JeecgBootException("ES索引设置异常");
                }
            } else {
                log.error("settingPath in @Setting has to be defined.");
                throw new JeecgBootException("ES索引配置不存在");
            }
        } else {
            log.error("@Setting has to be defined.");
            throw new JeecgBootException("@Setting has to be defined");
        }
    }

    public String getRelIndexName(Class<? extends EsEntity> clazz, String yyyymm) {
        String prefixIndexName = this.elasticsearchRestTemplate.getPrefixIndexName(clazz);
        return prefixIndexName + "_" + EsIndexUtil.getTenantId() + "_" + yyyymm;
    }

    static {
        GET_INDEX_CACHE = CacheBuilder.newBuilder().concurrencyLevel(32).initialCapacity(64).maximumSize(1024L)
            .expireAfterWrite(3600L, TimeUnit.SECONDS).build();
    }
}
